#ifndef COMM_ARBITER_H
#define COMM_ARBITER_H 1

#include "kern_ll.h"
#include <pthread.h>
#include <semaphore.h>
#include "comm_mem.h"
// remove definition to remove debug capabilities
//#define DEBUG 1

// definitions below



// priorities for queuing reads
#define HIGH_PRIORITY 0
#define LOW_PRIORITY 1

#define NOWAIT 0
#define WAIT   1


// this is essentially the communication arbiter
// should be file agnostic. only performed in place of an actual
// read


// TODO: implement timer for the ref counter 
// for freeing memory
// traverse the list and delete memory for those whose
// ref_count == 0

// TODO: modify linked lists so that multiple 
// images can be accessed

// memory region structure. 
typedef struct
{
	// should be the domain of the image file
	u_int64_t offset;
	u_int64_t size_allocated;
	// reference counter, once zero, can be freed upon timer signal
	unsigned int ref_count;
	LINKEDLIST list;
	LINKEDLIST contiguous; // ptr to contiguous memory
	void *ptr;
} MEM_REGION, *PMEMREGION;

// memory request structure.
typedef struct
{
	
	unsigned int fd;
	// should be the domain of the image file
	u_int64_t offset;
	u_int64_t size;
		
	LINKEDLIST queue_list;
	
	PMEMREGION associated_mem_region;
} MEM_REQUEST, *PMEMREQUEST;

typedef struct
{
	void *work_ptr;
	LINKEDLIST queue_list;
} WORK_OBJECT, *PWORK_OBJECT;

void *pf_pop_work();
void *pf_lock_work();
void pf_free_work();
void pf_push_work(void *ptr);
// defines above

// global variables below

PLINKEDLIST allocated_regions; // regions of allocated memory
PLINKEDLIST reg_allocation_queue; // regions of allocated memory
PLINKEDLIST reg_allocation_queue_tail; // regions of allocated memory
PLINKEDLIST high_allocation_queue; // regions of allocated memory

sem_t request_sem;

pthread_cond_t queue_wait;
pthread_cond_t arbiter_wait;

pthread_mutex_t mem_mutex;
pthread_mutex_t queue_mutex;
pthread_condattr_t queue_wait_attr;
pthread_t comm_arbiter_thread;

pthread_mutex_t work_mutex;

// global variables above

u_int64_t slab_size;
unsigned int slab_read_ahead;
unsigned int slab_read_behind;

unsigned int reads;

unsigned int com_id;

// functions below

// work queues
PLINKEDLIST pfm_queue_head;
PLINKEDLIST pfm_queue_tail;

//PPFM_FOREMOST get_queue();
//void queue_chunk(PPFM_FOREMOST new_chunk);




unsigned int init_comm_arbiter(unsigned int size);

PMEMREGION within_previous_region(unsigned int fd, u_int64_t offset, u_int64_t size);

// part of the communication arbiter is to control access to the disk
// certain accesses will have priority, eg, those that control access
// to the FAT or supernodes, thus they will occur first
// following those reads, then the reads will occur in sequential order
PMEMREGION queue_and_wait(unsigned int fd, u_int64_t offset, u_int64_t size, unsigned char read_priority);

PMEMREGION allocate_img_region(unsigned int fd, u_int64_t offset, u_int64_t size, unsigned char read_priority);

PMEMREGION allocate_queue_entry(unsigned int fd, u_int64_t offset, u_int64_t size);

// returns the actual size read
// should nearly always equal input size
u_int64_t access_img_region(unsigned int fd, u_int64_t offset, u_int64_t size, char *buf, unsigned char read_priority);

void free_region(unsigned int fd,u_int64_t offset);

PLINKEDLIST arbitrate_queue(PLINKEDLIST list, unsigned char type);

u_int64_t append_contiguous_region(int fd,PMEMREGION ptr,u_int64_t offset, u_int64_t size, char *buf, u_int64_t actual_size);

void *comm_arbiter_main();

u_int64_t access_img_region_ptr(unsigned int fd, u_int64_t offset, u_int64_t size, char **buf, unsigned char read_priority);


#endif
